﻿using System;
using System.ComponentModel.Composition;
using System.Threading;
using Cubicle.Core.Interfaces;

namespace Cubicle.Core.Processors.Invoke
{
    /// <summary>
    /// The invocation engine is a loosely coupled set of processer elements
    /// interconnected with the "chain of responsibility" pattern.
    /// 
    /// The processer elements are stringed together to form "pipelines" and
    /// these pipelines are stringed together to form the engine as a whole.
    /// 
    /// Each pipeline is responsible for a specific task.
    /// </summary>
    [Export("Cubicle.Processors.Invoke.InstructionProcessor", typeof(IProcessor<object>))]
    [ExportMetadata("Name", "InstructionProcessor")]
    [ExportMetadata("Description", "Marks the start of the InstructionRequest pipeline. Responsible for BeforeExecute / AfterExecute events.")]
    public class InstructionProcessor : ProcessorQueue<object, InstructionRequest>
    {

        [Export("Id")]
        private static ObjectWrapper<int> _id = new ObjectWrapper<int>();

        protected override InstructionRequest ConvertTo(object input)
        {
            var request = (Instruction)input;
            return new InstructionRequest(request) { ExecutingThread = Thread.CurrentThread };
        }

        protected override object ConvertFrom(InstructionRequest input)
        {
            input.Inst.Result = input.Result();
            return input.Result();
        }

        public override InstructionRequest ProcessQueue(InstructionRequest request)
        {
            try
            {
                request.Id = Interlocked.Increment(ref _id.Value);
                request.ExecutingThread = Thread.CurrentThread;
                request.Inst.StartTime = DateTime.Now;

                // Before execute event
                var eventArgs = new InstructionEventArgs(InstructionEventType.BeforeExecute, request);
                RaiseProcessorEvent(this, eventArgs);

                // Perform processing here
                request = base.ProcessQueue(request);

                // Mark as complete
                request.Type(RequestType.Completed);
            }
            catch (Exception e)
            {
                request.ExceptionInfo = e;
                var eventArgs = new InstructionEventArgs(InstructionEventType.ExecuteException, request);
                RaiseProcessorEvent(this, eventArgs);

                // Mark as failed
                request.Type(RequestType.Failed);
            }
            finally
            {
                // After execute event
                var eventArgs = new InstructionEventArgs(InstructionEventType.AfterExecute, request);
                RaiseProcessorEvent(this, eventArgs);
            }

            // Close instruction
            request.ExecutingThread = null;
            request.Inst.EndTime = DateTime.Now;
            return request;
        }

    }

}
